//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"

//[output] configuration section
//
//==============================================================================
//layout information
//==============================================================================
//layout="row"|"float" will default to row if not present
//
//------------------------------------------------------------------------------
//row related keys/values 
//------------------------------------------------------------------------------
//row_index=index with no index the output will be added on the right and side
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
//float related keys/values
//------------------------------------------------------------------------------
//x=top left corner x coordinate in compositor space
//y=top left corner y coordinate in compositor space
//------------------------------------------------------------------------------
//==============================================================================
//
//==============================================================================
//mode information
//==============================================================================
//mode=pixel_widthxpixel_height@refresh
//==============================================================================
//
//==============================================================================
//glob
//==============================================================================
//those are globs, case sensisitve, non extended (for now)
//manufacturer_glob
//product_code_glob
//monitor_name_glob
//monitor_serial_glob
//==============================================================================

#define LOCAL_ERR(fmt,...) \
{\
void *sysname=(void*)udev_device_get_sysname(\
registry[c->output_name].output.d);\
PERR("cfg:output:%s:" fmt,sysname,##__VA_ARGS__);\
}

#define LOCAL_LOG(fmt,...) \
{\
void *sysname=(void*)udev_device_get_sysname(\
registry[c->output_name].output.d);\
LOG_CFG("output:%s:" fmt,sysname,##__VA_ARGS__);\
}

#if LOG_CFG_LVL>=2
#define LOCAL_LOG_LVL2(fmt,...) LOCAL_LOG(fmt,##__VA_ARGS__);
#else
#define LOCAL_LOG_LVL2 (void)0;
#endif

#if LOG_CFG_LVL>=2
#define LOCAL_LOG_LVL2_DUMP_LINE \
{\
u8 line_buf[129];\
memset(&line_buf[0],0,129);\
u8 sz=line_end-line_start;\
if(sz>128) sz=128;\
memcpy(&line_buf[0],c->c.m+line_start,sz);\
LOCAL_LOG("%ld \"%s\"\n",line_start,&line_buf[0]);\
}
#else
#define LOCAL_LOG_LVL2_DUMP_LINE
#endif

//return an offset on the first char of the first line of the next output
//section following of offset in mmaped file
#define OUTPUT_SECTION_STR "[output]"
#define NO_MORE_OUTPUTS -1
static s64 next(struct cfg_ctx_output *c,s64 line_start)
{
  while(1){
    if((u64)line_start>=c->c.sz){
      LOCAL_LOG_LVL2("no more output section:line start not in file\n");
      return NO_MORE_OUTPUTS;
    }

    s64 line_end=cfg_line_end_reach(&c->c,line_start);
    LOCAL_LOG_LVL2_DUMP_LINE;

    s64 key_start=cfg_blanks_skip(&c->c,line_start,line_end);
    s64 key_end=cfg_non_blanks_skip(&c->c,key_start,line_end);
    
    if(key_end-key_start==cs_n(OUTPUT_SECTION_STR)&&!strncmp(c->c.m+key_start,
                                         OUTPUT_SECTION_STR,key_end-key_start)){
        LOCAL_LOG_LVL2("output section found at %ld\n",key_start);
        return line_end+1;
    }

    line_start=line_end+1;
  }
}

#define MATCH_ERR -1
#define NO_MATCH   0
#define MATCH      1
#define SKIP       2
static s8 glob(struct cfg_ctx_output *c,void *name,u8 *glob,u8 *str)
{
  if(glob[0]){
    s8 r=ulinux_match(glob,str,0);
    if(r==ULINUX_MATCH_NOMATCH) goto exit_no_match;
    else if(r!=ULINUX_MATCH_MATCH) goto exit_err;
    goto exit_match;
  }
  return SKIP;

exit_match:
  LOCAL_LOG("%s:'%s' matched '%s'\n",name,glob,str);
  return MATCH;

exit_no_match:
  LOCAL_LOG("%s:'%s' did not match '%s'\n",name,glob,str);
  return NO_MATCH;

exit_err:
  LOCAL_ERR("%s:error while matching '%s' with '%s'\n",name,glob,str);
  return MATCH_ERR;
}

static s8 match(struct cfg_ctx_output *c,s64 output_start)
{
  struct cfg_output_match_info info;
  cfg_output_match_info_get(c,output_start,&info);

  s8 r=glob(c,"manufacturer",&info.manufacturer_glob[0],
                              &registry[c->output_name].output.manufacturer[0]);
  if(r!=MATCH&&r!=SKIP) return r;

  r=glob(c,"product code",&info.product_code_glob[0],
                              &registry[c->output_name].output.product_code[0]);
  if(r!=MATCH&&r!=SKIP) return r;

  r=glob(c,"monitor name",&info.monitor_name_glob[0],
                              &registry[c->output_name].output.monitor_name[0]);
  if(r!=MATCH&&r!=SKIP) return r;

  r=glob(c,"monitor serial",&info.monitor_serial_glob[0],
                            &registry[c->output_name].output.monitor_serial[0]);
  if(r!=MATCH&&r!=SKIP) return r;
  return MATCH;
}

#define LOOKUP_ERR       -2
#define LOOKUP_NOT_FOUND -1
static s64 output_lookup(struct cfg_ctx_output *c)
{
  s64 output_start=0;
  while(1){
    output_start=next(c,output_start);
    if(output_start==NO_MORE_OUTPUTS){
      LOCAL_LOG("no matching output section was found\n");
      return LOOKUP_NOT_FOUND;
    }

    s8 r=match(c,output_start);
    if(r==MATCH){
      LOCAL_LOG("a matching output section was found\n");
      return output_start;
    }
    if(r==MATCH_ERR){
      return LOOKUP_ERR;
    }
  }
}

void cfg_output_info_get(s32 output_name,struct cfg_output_info *info)
{
  struct cfg_ctx_output c;
  c.output_name=output_name;

  cfg_output_layout_info_defaults(&info->layout);
  memset(info->mode,0,sizeof("hhhhxvvvv@rrr"));

  s8 r=cfg_file_open_ro(&c.c);
  if(r==LWL_ERR) return;

  s64 output_start=output_lookup(&c);
  if(output_start>=0){
    cfg_output_layout_info_get(&c,output_start,&info->layout);
    cfg_output_mode_get(&c,output_start,&info->mode[0]);
  }
  cfg_file_close(&c.c);
}
